package com.jack.screenprojectionpushsimple

import android.hardware.display.DisplayManager
import android.media.MediaCodec
import android.media.MediaCodecInfo
import android.media.MediaFormat
import android.media.projection.MediaProjection
import android.view.Surface
import java.nio.ByteBuffer


/**
 * @创建者 Jack
 * @创建时间 2024/1/13 9:17
 * @描述
 */
class H265Encoder(
    mediaProjection: MediaProjection,
    socketClient: SocketClient,
    width: Int,
    height: Int
) : Thread() {
    private lateinit var mMediaCodec: MediaCodec
    private var mSocketClient: SocketClient

    init {
        mSocketClient = socketClient
        try {
            //1.创建MediaCodec
            mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_HEVC)
            //2.1.mediaformat:用于指定媒体格式，包括音视频的编码格式、采样率、帧率等信息。它描述了输入数据的格式，以便编解码器能够正确地处理数据。
            val mediaformat =
                MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_HEVC, width, height)

            mediaformat.setInteger(
                MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface
            )
            //在使用 MediaCodec 进行编码时，MediaFormat.KEY_FRAME_RATE 主要用于指定期望的编码帧率。设置这个值不会直接影响播放速度，而是告诉编码器生成视频帧的速率。
            mediaformat.setInteger(MediaFormat.KEY_FRAME_RATE, 15)
            //码率
            mediaformat.setInteger(MediaFormat.KEY_BIT_RATE, 400000)
            //设置2s一个I帧
            mediaformat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2)

            //2.配置编解码器的参数
            //2.2.参数2：该参数用于指定一个Surface对象，如果你想在解码过程中将解码后的视频帧渲染到屏幕上，
            // 可以通过这个Surface进行渲染。如果你只是进行音频解码，可以将此参数设置为null。
            //2.3.参数3：如果你需要对音视频进行数字版权管理（DRM）或其他加密操作，可以通过这个参数传递相应的MediaCrypto对象。如果不需要加密，可以将此参数设置为null。
            //2.4.参数4：将 flags 设置为 0 表明不启用任何附加的特性或标志，仅使用默认的配置。
            //通过设置 CONFIGURE_FLAG_ENCODE 来表示这是一个编码器，或者设置 CONFIGURE_FLAG_SECURE 来表示需要安全的解码。但如果没有这些特殊需求，通常可以将 flags 参数保持为 0。
            mMediaCodec.configure(mediaformat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)

            //4.获取mediaCodec的surface
            val surface: Surface = mMediaCodec.createInputSurface()

            //5.创建一个虚拟显示，通常用于屏幕录制
            //参数1：虚拟显示的名称
            //参数2，3：虚拟显示的宽度和高度
            //参数4传入1：表示1dp = 1px（虚拟显示的每英寸点数）
            //参数5：标志位，指定创建虚拟显示的一些选项。
            //参数6：用于接收虚拟显示状态变化的回调接口
            //参数7：指定用于处理回调的 Handler
            mediaProjection.createVirtualDisplay(
                "screen-encoder",
                width, height, 1,
                DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                surface, null, null
            )
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    fun startPush() {
        start()
    }

    override fun run() {
        //6.开始编码
        mMediaCodec.start()

        val bufferInfo = MediaCodec.BufferInfo()
        while (true) {
            //7.查询编码好的数据
            val index: Int = mMediaCodec.dequeueOutputBuffer(bufferInfo, 100000)
            if (index >= 0) {
                //8.获取对应的输出缓冲区，并从中读取解码后的数据
                val buffer: ByteBuffer? = mMediaCodec.getOutputBuffer(index)
                if (buffer != null) {
                    handleFrame(buffer, bufferInfo);
                }
                mMediaCodec.releaseOutputBuffer(index, false)
            }
        }
    }

    private val I = 19
    private val VPS = 32
    private lateinit var vps_sps_pps_buf: ByteArray

    private fun handleFrame(buffer: ByteBuffer, bufferInfo: MediaCodec.BufferInfo) {
        println("handleFrame >>> ")
        var offset = 4
        if (buffer[2].toInt() == 0x01) {
            offset = 3
        }
        when (buffer[offset].toInt() and 0x7E shr 1) {
            VPS -> {
                vps_sps_pps_buf = ByteArray(bufferInfo.size)
                buffer[vps_sps_pps_buf]
            }

            I -> {
                val bytes = ByteArray(bufferInfo.size)
                buffer[bytes]
                val newBuf = ByteArray(vps_sps_pps_buf.size + bytes.size)
                System.arraycopy(vps_sps_pps_buf, 0, newBuf, 0, vps_sps_pps_buf.size)
                System.arraycopy(bytes, 0, newBuf, vps_sps_pps_buf.size, bytes.size)
                mSocketClient.pushData(newBuf)
            }

            else -> {
                val bytes = ByteArray(bufferInfo.size)
                buffer[bytes]
                mSocketClient.pushData(bytes)
            }
        }
    }

}